
/*							*/
/*  Indexed Sequential Access Method			*/
/*  Relational Database Systems, Inc.			*/
/*							*/
/*		Proprietary Data			*/
/*    This document contains proprietary information	*/
/* and trade secrets which are the property of		*/
/* Relational Database Systems, Inc. (RDSI). This	*/
/* document is submitted to recipient in confidence.	*/
/* The information contained herein may not be used,	*/
/* copied or disclosed in whole or in part except as	*/
/* permitted by written agreement signed by an officer	*/
/* of RDSI.						*/
/*							*/

/*
 * C-ISAM bcheck utility program
 *
 * Relational Database Systems, Inc.
 * Roy Harrington   --   August 30, 1982
 *
 *  version history
 *    1.00  08/30/82  RVH  originally written
 *    1.01  05/17/83  RVH  V101
 *			-- fix bug in makindex (err from kysearch)
 *    1.02  08/15/83  RVH  V102  change argument to bfflush
 *			         allow block devices for data/index files
 *				 check dupnum's in key order checks
 *    1.03  02/01/84  RVH  V103  add checks for last item in
 *				 internal b-tree nodes
 */

#include "btree.h"
#include <stdio.h>

#define reterr(x)	{ err(x); return(-1); }

int level = 0;		/* B-tree level			*/

char iflag = 0;		/* check index file only	*/
char lflag = 0;		/* long listing => print keys	*/
char nflag = 0;		/* answer no to all questions	*/
char yflag = 0;		/* answer yes to all questions	*/

char *idxmap = 0;	/* pointer to index bit map	*/
char *datmap = 0;	/* pointer to data bit map	*/
char *idxmap2 = 0;	/* pointer to index bit map	*/
char *datmap2 = 0;	/* pointer to data bit map	*/
char *idxflag = 0;	/* flags for all indexes	*/

int idxsiz = 0;		/* size of index bit map (bytes)*/
int datsiz = 0;		/* size of data bit map	(bytes)	*/

long idxlast = 0;	/* last record used in index	*/
long datlast = 0;	/* last record used in data	*/

long badindx = 0;	/* # of bad index nodes		*/
long dupindx = 0;	/* # of duplicate index nodes	*/
long misindx = 0;	/* # of missing index nodes	*/

long baddata = 0;	/* # of bad data records	*/
long dupdata = 0;	/* # of duplicate data records	*/
long misdata = 0;	/* # of missing data records	*/

char badindex = 0;	/* flag indicating bad index	*/
char badorder = 0;	/* bad index key order		*/
char badifree = 0;	/* bad index node free list	*/
char baddfree = 0;	/* bad data record free list	*/


#define	CK_OK	0	/* index all right			*/
#define CK_ERR	1	/* delete index	(fatal errors, no fix)	*/
#define CK_MAKE	2	/* recreate index (fatal errors)	*/
#define CK_PAT	3	/* patch index (missing & deleted recs)	*/

extern struct ifile *filep;
extern struct open *openp;
extern struct dict *dictp;
extern char temprecord[];

#ifdef lint		/* keep lint from complaining	*/
extern int _VOID_;
#endif

long cntmap();
char *malloc();

main(argc, argv)
int argc;
char *argv[];
{
	register int i;
	register char *ap;
	long used;

	printf("\nBCHECK  C-ISAM B-tree Checker version 1.03\n");
	printf("%s\n%s\n\n", iscopyright, isserial);

	if (argc <= 1)
	    {
	    printf("usage: bcheck -ilny cisamfiles ...\n");
	    printf("      -i   check index file only\n");
	    printf("      -l   list entries in b-trees\n");
	    printf("      -n   answer no to all questions\n");
	    printf("      -y   answer yes to all questions\n");
	    printf("\n");
	    exit(1);
	    }

	ap = *++argv;
	if (*ap == '-')
	    {
	    argv++; argc--;
	    while (*++ap)
		switch (*ap)
		    {
		    case 'i': iflag++; break;
		    case 'l': lflag++; break;
		    case 'n': nflag++; break;
		    case 'y': yflag++; break;
		    default:
			printf("ERROR: illegal option -%c\n", *ap);
			break;
		    }
	    }

	for (i=1; i<argc; i++, argv++)
	    {
	    printf("\nC-ISAM File: %s\n\n", *argv);
	    if (ckfile(*argv) == 0)
		{
		used = cntmap(idxmap, idxsiz);
		printf("%ld index node(s) used, %ld free -- ",
				used, idxlast-used);
		used = cntmap(datmap, datsiz);
		printf("%ld data record(s) used, %ld free\n",
				used, datlast-used);
		}
	    }
	printf("\n");
}

/*
 * check a C-ISAM file
 */

ckfile(filename)
char *filename;
{
	int mode, i, cc;
	register char *name;

	for (name = filename; *name; name++);	/* look for .dat & .idx	*/
	name -= 4;
	if (stcmpr(name, ".dat") == 0 || stcmpr(name, ".idx") == 0)
	    *name = 0;

	if ((i = allocopen()) < 0)		/* allocate open struct	*/
	    return(-1);
	
	mode = (nflag ? ISINPUT : ISINOUT);
	openp->o_mode = mode + ISEXCLLOCK;
	filep = openp->o_filep = allocfile(filename, mode);
	if (filep == NULL)
	    {
	    err("cannot open C-ISAM file");
	    freeopen(openp);
	    return(-1);
	    }

	if (lksemaphore(LOCKUNCOND) == -1)
	    goto prtexit;

	cc = lkopenlock(ISEXCLLOCK);
	VOID lksemaphore(UNLOCK);
	if (cc == -1) goto prtexit;

	filep->f_dicbp = bfread(1L);		/* read dictionary	*/
	if (filep->f_dicbp == NULL)
	    {
	    VOID lkopenlock(UNLOCK);
	    goto prtexit;
	    }

	dictp = filep->f_dicbp->bf_un.bf_dict;

	if (ckdict()) goto retexit;		/* check dictionary	*/

	ckdata();				/* check data file	*/
	if (ckindex()) goto retexit;		/* check all indexes	*/
	ckdfree();				/* check data free list	*/
	ckifree();				/* check index free list*/

	if (badifree == CK_MAKE) makifree();	/* recreate free lists	*/
	if (baddfree == CK_MAKE) makdfree();

	for (i = ld2(dictp->d_nkeys); i>0; i--)
	    {
	    switch (idxflag[i])
		{
		case CK_OK:	break;
		case CK_ERR:	delindex(i); break;
		case CK_MAKE:	makindex(i); break;
		}
	    }
	cc = 0;
retrn:
	VOID lksemaphore(LOCKUNCOND);
	VOID lkopenlock(UNLOCK);
	VOID isexit();
	bfflush(filep);				/* V102			*/
	if (openp->o_flags & OSLOCK)		/* semaphore lock on ?	*/
	    VOID lksemaphore(UNLOCK);		/* yes, release it	*/
	freeopen(openp);
	return(cc);

retexit:
	cc = -1;
	goto retrn;
prtexit:
	prterr();
	freeopen(openp);
	return(-1);
}

/*
 * check dictionary
 */

ckdict()
{
	register int i;
	long recnum, filsize;
	register struct buff *bp;
	struct stat st;

	printf("** Check Dictionary and File Sizes\n");

	i = ld2(dictp->d_magic) & 0177777;
	if (i != ISMAGIC)
	    reterr("bad magic number");

	filep->f_idxsize = i = ld2(dictp->d_idxsize);
	if (i > NODESIZE || i < 127)
	    reterr("bad index record size");

	filep->f_datsize = i = ld2(dictp->d_recsize);
	if (i > RECORDSIZE || i < 1)
	    reterr("bad data record size");

	filsize = lseek(filep->f_idxfd, 0L, 2) / (filep->f_idxsize+1);
	idxlast = ld4(dictp->d_nextnode);
	fstat(filep->f_idxfd, &st);			/* V102  */
	if (idxlast > filsize && (st.st_mode & S_IFMT) == S_IFREG)
	    {
	    err("warning: bad index file size");
	    idxlast = filsize;
	    if (ask("Fix size ? "))
		{
		st4(idxlast, dictp->d_nextnode);
		filep->f_dicbp->bf_flags |= BFMODF;
		}
	    }
	idxsiz = (idxlast>>3) + 1;

	filsize = lseek(filep->f_datfd, 0L, 2) / (filep->f_datsize+1);
	datlast = ld4(dictp->d_nextdata);
	fstat(filep->f_datfd, &st);			/* V102  */
	if (datlast > filsize && (st.st_mode & S_IFMT) == S_IFREG)
	    {
	    err("warning: bad data file size");
	    datlast = filsize;
	    if (ask("Fix size ? "))
		{
		st4(datlast, dictp->d_nextdata);
		filep->f_dicbp->bf_flags |= BFMODF;
		}
	    }
	datsiz = (datlast>>3) + 1;

	if (idxmap != NULL) free(idxmap);	/* free old bit maps	*/
	if (datmap != NULL) free(datmap);
	if (idxmap2 != NULL) free(idxmap2);
	if (datmap2 != NULL) free(datmap2);
	if (idxflag != NULL) free(idxflag);

	idxmap = malloc(CASTUN idxsiz);		/* allocate bit maps	*/
	datmap = malloc(CASTUN datsiz);
	idxmap2 = malloc(CASTUN idxsiz);
	datmap2 = malloc(CASTUN datsiz);
	idxflag = malloc(CASTUN ld2(dictp->d_nkeys)+3);

	if (idxmap == NULL || datmap == NULL ||
	        idxmap2 == NULL || datmap2 == NULL || idxflag == NULL)
	    reterr("cannot allocate main memory");

	clrmap(idxmap, idxsiz);			/* clear all bits	*/
	clrmap(datmap, datsiz);
	clrmap(idxmap2, idxsiz);
	clrmap(datmap2, datsiz);

	VOID setbit(idxmap, 1L);		/* dictionary node used	*/
	recnum = ld4(dictp->d_audit) & 0x7FFFFFFF;
						/* audit trail node	*/
	if (recnum != 0) VOID setbit(idxmap, recnum);
	recnum = ld4(dictp->d_keysnode);
	while (recnum != 0)			/* key descriptors	*/
	    {
	    if (recnum <= 0 || recnum > idxlast)
		reterr("bad index node in key descriptors");
	    if (setbit(idxmap, recnum))
		reterr("duplicate index node in key descriptors");
	    bp = bfread(recnum);
	    if (bp == NULL)
		{ prterr(); break; }
	    recnum = ld4(bp->bf_un.bf_keyn->kn_next);
	    bfrelease(bp);
	    }

	return(0);
}

/*
 * check data file
 *	read each data record and
 *	save which records are being used
 *	in "datmap" bit map
 */

ckdata()
{
	long recnum, freenode;
	register int cc, used;
	register char *fp;
	register struct buff *bp;

	if (!iflag)
	    {
	    printf("** Check Data File Records\n");

	    for (recnum = 1L; recnum <= datlast; recnum++)
		{
		cc = dataread(temprecord, recnum); /* read data record	*/
		if (cc == 0)
		    VOID setbit(datmap, recnum);  /* mark record in use	*/
		else if (cc != -1)
		    err("bad data record");
		}
	    return;
	    }

	freenode = ld4(dictp->d_freedata);
	setmap(datmap, datsiz-1);
	for (recnum = datlast & ~07; recnum <= datlast; recnum++)
	    VOID setbit(datmap, recnum);
	VOID clrbit(datmap, 0L);

	while (freenode != 0)
	    {
	    if (freenode <= 0 || freenode > idxlast)
		break;				/* illegal index node	*/
	    bp = bfread(freenode);		/* read free list node	*/
	    if (bp == NULL)
		{ prterr(); break; }
	    used = bp->bf_used - 6;
	    if (used < 0 || used > filep->f_idxsize-8)
		{
		err("bad data free list format");
		break;
		}
	    fp = bp->bf_un.bf_free->fn_free[0];
	    while (used > 0)
		{
		recnum = ld4(fp);
		if (recnum > 0 && recnum <= datlast)
		    VOID clrbit(datmap, recnum);
		fp += 4;
		used -= 4;
		}
	    freenode = ld4(bp->bf_un.bf_free->fn_next);
	    bfrelease(bp);
	    }

}

/*
 * check indexes
 */

ckindex()
{
	struct keydesc key;
	register int i, keynum;
	register struct keypart *keyp;
	struct item *nullip;

	printf("** Check Indexes and Key Descriptions\n");

	for (keynum=1; ; keynum++)
	    {
	    idxflag[keynum] = CK_OK;
	    if (kysearch(&key, keynum) < 0)
		break;
	    if (kylegal(&key, filep->f_datsize))
		{
		err("bad key description format");
		return(-1);
		}
	    printf("**   Index %d = %s  ", keynum,
			key.k_flags & ISDUPS ? "duplicates" : "unique key");
	    keyp = &key.k_part[0];
	    for (i=0; i<key.k_nparts; i++, keyp++)
		{
		printf("(%d,%d,%d) ",
		    keyp->kp_start, keyp->kp_leng, keyp->kp_type);
		}
	    printf("\n");
	    level = 0;

	    clrmap(idxmap2, idxsiz);
	    clrmap(datmap2, datsiz);

	    badindex = badorder = 0;
	    badindx = dupindx = baddata = dupdata = 0L;

	    nullip = NULL;
	    cktree(key.k_rootnode, &key, nullip, nullip);

	    misdata = cntmap(datmap, datsiz) - cntmap(datmap2, datsiz);
	    if (badindx || dupindx || baddata || dupdata || misdata || badorder)
		{
		if (badindx) errn("%ld bad index node(s)", badindx);
		if (dupindx) errn("%ld duplicate index node(s)", dupindx);
		if (baddata) errn("%ld bad data record(s)", baddata);
		if (dupdata) errn("%ld duplicate data record(s)", dupdata);
		if (misdata) errn("%ld missing data record(s)", misdata);
		if (badorder) err("index out of order");
		badindex = 1;
		}

	    if (badindex)
		{
	        if (ask("Delete index ? "))
		    {
		    clrmap(idxmap2, idxsiz);
		    idxflag[keynum] = CK_ERR;
		    if (ask("Remake index ? "))
			{
			VOID setbit(idxmap2, key.k_rootnode);
			idxflag[keynum] = CK_MAKE;
			}
		    }
		}

	    ormap(idxmap, idxmap2, idxsiz);
	    }

	if (--keynum != ld2(dictp->d_nkeys))
	    {
	    err("bad number of indexes");
	    if (ask("Fix number ? "))
		{
		st2(keynum, dictp->d_nkeys);
		filep->f_dicbp->bf_flags |= BFMODF;
		}
	    }
	return(0);
}

/*
 * check a B-tree node
 *	node  =  index node number
 *	keyd  -> current keydesc
 *	minip -> minimum legal key value
 *	maxip -> maximum legal key value
 *
 *	calls itself recursively to go down B-tree
 */

cktree(node, keyd, minip, maxip)
long node;
struct keydesc *keyd;
struct item *minip;
struct item *maxip;
{
	struct item item, lastitem;
	struct buff *bp;
	char *np;
	register struct item *ip = &item;
	register int offset, nextoff, used, rel;

	if (node <= 0 || node > idxlast)	/* check for legal node	*/
	    { badindx++; return; }

	if (setbit(idxmap2, node))		/* check for dup node	*/
	    { dupindx++; return; }

	bp = bfread(node);			/* get node buffer	*/
	if (bp == NULL)
	    { prterr(); return; }

	np = bp->bf_un.bf_node->n_items;	/* get node pointer	*/
	used = bp->bf_used - 2;			/* 2 bytes for n_used	*/
	if (used < 0 || used > filep->f_idxsize-4)
	    goto badform;			/* bad format of node	*/

	if (lflag)				/* long listing ?	*/
	    {
	    printf("\nbtree level: %d, node: %ld, used: %d\n",
				level, node, used);
	    printf("flag totln keyln dupnm recptr  leadc tailc key\n");
	    }

	for (offset=0; offset < used; offset = nextoff)
	    {					/* get next item	*/
	    nextoff = itunpack(np, offset, ip, keyd);
	    if (ip->it_keylen < 0)
		goto badform;			/* bad format of node	*/

	    if (minip != NULL && (bp->bf_level == 0 || nextoff < used))
					/* V103 - check level & nextoff */
		{			/* V102	- check dupnums		*/
		rel = itcompare(minip, ip, keyd, 0);
		if (rel > 0 || (rel == 0 && minip->it_dupnum > ip->it_dupnum))
		    badorder = 1;
		}

	    if (bp->bf_level != 0)
		{
		level++;
		cktree(ip->it_ptr, keyd, minip, (nextoff < used) ? ip : maxip);
		level--;
		if (lflag) printf("\nbtree level: %d\n", level);
		}
	    else
		{
		if (ip->it_ptr <= 0 || ip->it_ptr > datlast ||
			tstbit(datmap, ip->it_ptr) == 0)
		    baddata++;
		else if (setbit(datmap2, ip->it_ptr))
		    dupdata++;
		}

	    if (lflag) pritem(ip, keyd);	/* long listing ?	*/

	    if (bp->bf_level == 0 || nextoff < used) /* V103 - added if	*/
		itcopy(ip, &lastitem, keyd);
	    minip = &lastitem;
	    }

	if (maxip != NULL)
	    {					/* V102	- check dupnums	*/
	    rel = itcompare(minip, maxip, keyd, 0); /* V103 - ip to minip */
	    if (rel > 0 || (rel == 0 && minip->it_dupnum > maxip->it_dupnum))
		badorder = 1;
	    }

	bfrelease(bp);
	return;

badform:
	errn("bad b-tree node (%ld) format", node);
	badindex = 1;
	bfrelease(bp);
}

/*
 * print out item
 */

pritem(ip, keyd)
register struct item *ip;
register struct keydesc *keyd;
{
	printf("%3o  %4d  %4d  %4d  %6ld  %4d  %4d  ",
		ip->it_flags, ip->it_totlen, ip->it_keylen,
		ip->it_dupnum, ip->it_ptr,
		ip->it_leadcnt, ip->it_trailcnt);
	switch(keyd->k_part[0].kp_type & 017)
	    {
	    case CHARTYPE:
		ip->it_key[keyd->k_len] = 0;
		printf("%s\n", ip->it_key);
		break;
	    case INTTYPE:
		printf("%d\n", ldint(ip->it_key));
		break;
	    case LONGTYPE:
		printf("%ld\n", ldlong(ip->it_key));
		break;
#ifndef NOFLOAT
	    case FLOATTYPE:
		printf("%f\n", ldfloat(ip->it_key));
		break;
	    case DOUBLETYPE:
		printf("%f\n", lddbl(ip->it_key));
		break;
#endif /* NOFLOAT */
	    }
}

/*
 * check index node free list
 */

ckifree()
{
	register char *fp;
	register int used;
	long freenode, recnum;
	register struct buff *bp;

	clrmap(idxmap2, idxsiz);
	dupindx = badindx = misindx = 0;
	freenode = ld4(dictp->d_freenode);

	while (freenode != 0)
	    {
	    if (freenode <= 0 || freenode > idxlast)
		{ badindx++; break; }		/* illegal index node	*/
	    if (setbit(idxmap2, freenode) || tstbit(idxmap, freenode))
		{ dupindx++; break; }

	    bp = bfread(freenode);		/* read free list node	*/
	    if (bp == NULL)
		{
		prterr();
		badifree = CK_ERR;
		break;
		}
	    used = bp->bf_used - 6;
	    if (used < 0 || used > filep->f_idxsize-8)
		{
		err("bad index free list format");
		badifree = CK_ERR;
		break;
		}
	    fp = bp->bf_un.bf_free->fn_free[0];
	    while (used > 0)
		{
		recnum = ld4(fp);
		if (recnum <= 0 || recnum > idxlast)
		    badindx++;
		else if (setbit(idxmap2, recnum) || tstbit(idxmap, recnum))
		    dupindx++;
		fp += 4;
		used -= 4;
		}
	    freenode = ld4(bp->bf_un.bf_free->fn_next);
	    bfrelease(bp);
	    }

	ormap(idxmap2, idxmap, idxsiz);
	misindx = idxlast - cntmap(idxmap2, idxsiz);

	if (badindx || dupindx || misindx)
	    {
	    if (badindx) errn("%ld bad index node(s)", badindx);
	    if (dupindx) errn("%ld duplicate index node(s)", dupindx);
	    if (misindx) errn("%ld missing index node(s)", misindx);
	    badifree = CK_ERR;
	    }

	if (badifree && ask("Fix index node free list ? "))
	    {
	    clrmap(idxmap2, idxsiz);
	    badifree = CK_MAKE;
	    }

	ormap(idxmap, idxmap2, idxsiz);
}

/*
 * check data record free list
 */

ckdfree()
{
	register char *fp;
	register int used;
	long freenode, recnum;
	register struct buff *bp;

	printf("** Check Data Record and Index Node Free Lists\n");

	clrmap(datmap2, datsiz);
	badifree = baddfree = CK_OK;
	dupdata = baddata = misdata = 0;
	freenode = ld4(dictp->d_freedata);

	while (freenode != 0)
	    {
	    if (freenode <= 0 || freenode > idxlast)
		{				/* illegal index node	*/
		err("bad index node");
		badifree = baddfree = CK_ERR;
		break;
		}
	    if (setbit(idxmap2, freenode) || tstbit(idxmap, freenode))
		{
		err("duplicate index node");
		badifree = baddfree = CK_ERR;
		break;
		}
	    bp = bfread(freenode);		/* read free list node	*/
	    if (bp == NULL)
		{
		prterr();
		baddfree = CK_ERR;
		break;
		}
	    used = bp->bf_used - 6;
	    if (used < 0 || used > filep->f_idxsize-8)
		{
		err("bad data record free list format");
		baddfree = CK_ERR;
		break;
		}
	    fp = bp->bf_un.bf_free->fn_free[0];
	    while (used > 0)
		{
		recnum = ld4(fp);
		if (recnum <= 0 || recnum > datlast)
		    baddata++;
		else if (setbit(datmap2, recnum) || tstbit(datmap, recnum))
		    dupdata++;
		fp += 4;
		used -= 4;
		}
	    freenode = ld4(bp->bf_un.bf_free->fn_next);
	    bfrelease(bp);
	    }

	ormap(datmap2, datmap, datsiz);
	misdata = datlast - cntmap(datmap2, datsiz);

	if (baddata || dupdata || misdata)
	    {
	    if (baddata) errn("%ld bad data record(s)", baddata);
	    if (dupdata) errn("%ld duplicate data record(s)", dupdata);
	    if (misdata) errn("%ld missing data record(s)", misdata);
	    baddfree = CK_ERR;
	    }

	if (baddfree && ask("Fix data record free list ? "))
	    {
	    clrmap(idxmap2, idxsiz);
	    baddfree = CK_MAKE;
	    }

	ormap(idxmap, idxmap2, idxsiz);
}

/*
 * recreate index node free list
 */

makifree()
{
	long recnum;

	printf("** Recreate Index Node Free List\n");

	st4(0L, dictp->d_freenode);		/* clear old free list	*/
	for (recnum = 1L; recnum <= idxlast; recnum++)
	    {
	    if (tstbit(idxmap, recnum) == 0)	/* index node free ?	*/
		freerec(recnum, INDXREC);
	    }
}


/*
 * recreate data record free list
 */

makdfree()
{
	long recnum;

	printf("** Recreate Data Record Free List\n");

	st4(0L, dictp->d_freedata);		/* clear old free list	*/
	for (recnum = 1L; recnum <= datlast; recnum++)
	    {
	    if (tstbit(datmap, recnum) == 0)	/* data record free ?	*/
		freerec(recnum, DATAREC);
	    }
}

/*
 * delete index from C-ISAM file
 */

delindex(keynum)
int keynum;
{
	struct keydesc key;

	printf("** Delete Index %d\n", keynum);

	if (kysearch(&key, keynum) < 0 || kydelete(&key))
	    prterr();
}

/*
 * recreate index of C-ISAM file
 */

makindex(keynum)
int keynum;
{
	register struct buff *bp;
	long recnum;
	struct keydesc key;
	struct item item;

	printf("** Recreate Index %d\n", keynum);

	if (kysearch(&key, keynum) < 0)		/* V101 added "< 0"	*/
	    {
	    err("cannot find key for index");
	    return;
	    }
	bp = bfgetclr(key.k_rootnode);		/* initialize root node	*/
	bp->bf_used = 2;
	bp->bf_level = 0;
	bp->bf_type = 0;
	bfdwrite(bp);

	for (recnum = 1L; recnum <= datlast; recnum++)
	    {
	    if (dataread(temprecord, recnum) < 0)
		continue;			/* deleted data record	*/
	    itmake(temprecord, &item, &key);
	    item.it_ptr = recnum;
	    if (btadditem(&item, &key))		/* add item to b-tree	*/
		{
		if (iserrno == EDUPL)
		    errn("duplicate key value, record %ld", recnum);
		else prterr();
		iserrno = 0;
		}
	    }
}


/*
 * set, clear and test bit in a map
 *
 *  for each routine:
 *	"map" points to char array bit map
 *	"num" is the bit number
 *	return value is always the previous value of bit
 */

setbit(map, num)
register char *map;
long num;
{
	register int retval, mask;

	map += (unsigned) (num>>3);
	mask = 1 << (num&7);
	retval = (*map & mask) != 0;
	*map |= mask;
	return(retval);
}

clrbit(map, num)
register char *map;
long num;
{
	register int retval, mask;

	map += (unsigned) (num>>3);
	mask = 1 << (num&7);
	retval = (*map & mask) != 0;
	*map &= ~mask;
	return(retval);
}

tstbit(map, num)
register char *map;
long num;
{
	register int retval, mask;

	map += (unsigned) (num>>3);
	mask = 1 << (num&7);
	retval = (*map & mask) != 0;
	return(retval);
}


/*
 * count bits in map
 *   returns number of bits on
 */

long cntmap(map, size)
register char *map;
register int size;
{
	register unsigned mask;
	long count = 0L;

	while (size--)
	    {
	    mask = (*map++ & 0377);
	    if (mask == 0377)
		count += 8;
	    else while (mask != 0)
		{
		if (mask & 1) count++;
		mask >>= 1;
		}
	    }
	return(count);
}
 
/*
 * or together two maps
 *  map1 = map1 | map2
 */

ormap(map1, map2, size)
register char *map1, *map2;
register int size;
{
	while (size--) *map1++ |= *map2++;
}

/*
 * clear map to all zeros
 */

clrmap(map, size)
register char *map;
register int size;
{
	while (size--) *map++ = 0;
}

/*
 * set map to all ones
 */

setmap(map, size)
register char *map;
register int size;
{
	while (size--) *map++ = ~0;
}

err(s)
char *s;
{
	printf("\nERROR: %s\n", s);
}

errn(s,n)
char *s;
long n;
{
	printf("\nERROR: ");
	printf(s, n);
	printf("\n");
}

#ifndef MPM86
#ifndef PCDOS
#ifndef	__USE_BSD	/* without this, causes definition duplicated error (by JewelJar) */
extern int sys_nerr;		/* number of system errors	*/
extern char *sys_errlist[];	/* system error messages	*/
#endif
#endif
#endif

prterr()
{
	register char *msg, *file;

	if (iserrno == 0) return;
	if (iserrno <= 99)
	    {
	    switch (iserrio & 0xF0)
		{
		case IO_OPEN: msg = "open";	break;
		case IO_CREA: msg = "creat";	break;
		case IO_SEEK: msg = "lseek";	break;
		case IO_READ: msg = "read";	break;
		case IO_WRIT: msg = "write";	break;
		case IO_LOCK: msg = "locking";	break;
		case IO_IOCTL:msg = "ioctl";	break;
		default:      msg = "";		break;
		}
	    switch (iserrio & 0x0F)
		{
		case IO_IDX: file = "index";	break;
		case IO_DAT: file = "data";	break;
		case IO_AUD: file = "audit";	break;
		case IO_LOK: file = "lock";	break;
		case IO_SEM: file = "semaphore"; break;
		default:     file = "";		break;
		}
	    printf("\nSYSTEM ERROR: ");
	    if (iserrio != 0) printf("%s(%s): ", msg, file);
#ifndef MPM86
#ifndef PCDOS
	    if (iserrno <= sys_nerr)
		printf("%s", sys_errlist[iserrno]);
#endif
#endif
	    printf("\n");
	    }
	else
	    {
	    switch (iserrno)
		{
		case EDUPL:	msg = "duplicate record";	break;
		case ENOTOPEN:	msg = "file not open";		break;
		case EBADARG:	msg = "illegal argument";	break;
		case EBADKEY:	msg = "illegal key desc";	break;
		case ETOOMANY:	msg = "too many files open";	break;
		case EBADFILE:	msg = "bad isam file format";	break;
		case ENOTEXCL:	msg = "non-exclusive access";	break;
		case ELOCKED:	msg = "record locked";		break;
		case EKEXISTS:	msg = "key already exists";	break;
		case EPRIMKEY:	msg = "is primary key";		break;
		case EENDFILE:	msg = "end/begin of file";	break;
		case ENOREC:	msg = "no record found";	break;
		case ENOCURR:	msg = "no current record";	break;
		case EFLOCKED:	msg = "file locked";		break;
		case EFNAME:	msg = "file name too long";	break;
		case ENOLOK:	msg = "can't create lock file";	break;
		default:	msg = "unknown error code";	break;
		}
	    printf("\nC-ISAM ERROR: %s\n", msg);
	    }
	iserrno = iserrio = 0;		/* clear error codes	*/
}

/*
 * ask user a question and get a yes or a no answer
 *   returns 0 => no
 *   returns 1 => yes
 */

ask(s)
char *s;
{
	char answer[10];

	printf(s);
	if (nflag)
	    {
	    printf("no\n\n");
	    return(0);
	    }
	if (yflag)
	    {
	    printf("yes\n\n");
	    return(1);
	    }
	for (;;)
	    {
	    if (fgets(answer, 10, stdin) == NULL) return(0);
	    printf("\n");
	    switch(answer[0])
		{
		case 'n':
		case 'N':
		    return(0);
		case 'y':
		case 'Y':
		    return(1);
		}
	    printf("Answer yes or no ? ");
	    }
/* NOTREACHED */
}
